package com.microweb.toastmaster.model;

import com.microweb.toastmaster.Constants;
import java.io.Serializable;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.userdetails.UserDetails;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class is used to generate Spring Validation rules
 * as well as the Hibernate mapping file.
 *
 * <p><a href="User.java.html"><i>View Source</i></a>
 *
 * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
 *         Updated by Dan Kibler (dan@getrolling.com)
 *  Extended to implement Acegi UserDetails interface
 *      by David Carter david@carter.net
 *
 * @hibernate.class table="app_user"
 */
public class User extends BaseObject implements Serializable, UserDetails {
    private static final long serialVersionUID = 3832626162173359411L;
    private static final Log log = LogFactory.getLog(User.class);

    protected Long id;
    protected String username;                    // required
    protected String password;                    // required
    protected String confirmPassword;
    protected String firstName;                   // required
    protected String lastName;                    // required
    protected String email;                       // required; unique
    protected String passwordHint;
    protected Integer version;
    protected Set<Role> roles = new HashSet<Role>();
    protected boolean enabled;
    protected boolean accountExpired;
    protected boolean accountLocked;
    protected boolean credentialsExpired;
    protected String phoneNumber;
    
    protected Set<?> speeches = new HashSet();
    protected Set<?> actors = new HashSet();
    
    private Date unvailableStartDate;
    private Date unvailableStopDate;
    
    public User() {}

    public User(String username) {
        this.username = username;
    }

    /**
     * @hibernate.id column="id" generator-class="native" unsaved-value="null"
     */
    public Long getId() {
        return id;
    }

    /**
     * @hibernate.property length="50" not-null="true" unique="true"
     */
    public String getUsername() {
        return username;
    }

    /**
     * @hibernate.property column="password" not-null="true"
     */
    public String getPassword() {
        return password;
    }

    public String getConfirmPassword() {
        return confirmPassword;
    }

    /**
     * @hibernate.property column="first_name" not-null="true" length="50"
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * @hibernate.property column="last_name" not-null="true" length="50"
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * Returns the full name.
     */
    public String getFullName() {
        return firstName + ' ' + lastName;
    }

    /**
     * @hibernate.property name="email" not-null="true" unique="true"
     */
    public String getEmail() {
        return email;
    }
    
    /**
     * @hibernate.property name="phoneNumber" not-null="false"
     */
    public String getPhoneNumber() {
        return phoneNumber;
    }

    /**
     * @hibernate.property column="password_hint" not-null="false"
     */
    public String getPasswordHint() {
        return passwordHint;
    }

    /**
     * @hibernate.set table="user_role" cascade="save-update" lazy="false"
     * @hibernate.collection-key column="user_id"
     * @hibernate.collection-many-to-many class="com.microweb.toastmaster.model.Role" column="role_id"
     */
    public Set<Role> getRoles() {
        return roles;
    }
   
    /**
     * @hibernate.set name="speeches" cascade="all" lazy="false" inverse="true"
     * @hibernate.key column="user_id"
     * @hibernate.one-to-many class="com.microweb.toastmaster.model.Speech"
     */
    public Set<?> getSpeeches() {
        return this.speeches;
    }
    
    public Date[] getOrderedSpeeches() {
        String[] projectNames = Constants.PROJECTNAMES;
        log.debug(username + " had delivered " + speeches.size() + " speeche(s)." );
        Date[] dates = new Date[Constants.PROJECTNAMES.length];
        try {
            for(int i=0; i<projectNames.length; i++) {
                Speech speech = getSpeechByProject(projectNames[i]);
                if (speech != null) {
                    log.debug("delieved speech " + projectNames[i] + " on " + speech.getDate());
                    dates[i] = speech.getDate();
                } else {
                    log.debug("does not delieve speech " + projectNames[i]);
                    dates[i] = null;
                }
            } 
        }catch (Exception ex) {
            ex.printStackTrace();
        }
        return dates;
    }
    
    /**
     * @hibernate.set name="actors" cascade="all" lazy="false" inverse="true"
     * @hibernate.key column="user_id"
     * @hibernate.one-to-many class="com.microweb.toastmaster.model.Actor"
     */
    public Set<?> getActors() {
        return this.actors;
    }

    /**
     * @hibernate.property column="unvailableStartDate"
     */
    public void setUnvailableStartDate(Date unvailableStartDate) {
        this.unvailableStartDate = unvailableStartDate;
    }

    public Date getUnvailableStartDate() {
        return unvailableStartDate;
    }

    /**
     * @hibernate.property column="unvailableStopDate"
     */
    public void setUnvailableStopDate(Date unvailableStopDate) {
        this.unvailableStopDate = unvailableStopDate;
    }

    public Date getUnvailableStopDate() {
        return unvailableStopDate;
    }

    /**
     * Adds a role for the user
     * @param role
     */
    public void addRole(Role role) {
        getRoles().add(role);
    }

    /**
     * @see org.acegisecurity.userdetails.UserDetails#getAuthorities()
     */
    public GrantedAuthority[] getAuthorities() {
        return (GrantedAuthority[]) roles.toArray(new GrantedAuthority[0]);
    }

    /**
     * @hibernate.version
     */
    public Integer getVersion() {
        return version;
    }
    
    /**
     * @hibernate.property column="account_enabled" type="yes_no"
     */
    public boolean isEnabled() {
        return enabled;
    }
    
    /**
     * @hibernate.property column="account_expired" not-null="true" type="yes_no"
     */
    public boolean isAccountExpired() {
        return accountExpired;
    }
    
    /**
     * @see org.acegisecurity.userdetails.UserDetails#isAccountNonExpired()
     */
    public boolean isAccountNonExpired() {
        return !isAccountExpired();
    }

    /**
     * @hibernate.property column="account_locked" not-null="true" type="yes_no"
     */
    public boolean isAccountLocked() {
        return accountLocked;
    }
    
    /**
     * @see org.acegisecurity.userdetails.UserDetails#isAccountNonLocked()
     */
    public boolean isAccountNonLocked() {
        return !isAccountLocked();
    }

    /**
     * @hibernate.property column="credentials_expired" not-null="true"  type="yes_no"
     */
    public boolean isCredentialsExpired() {
        return credentialsExpired;
    }
    
    /**
     * @see org.acegisecurity.userdetails.UserDetails#isCredentialsNonExpired()
     */
    public boolean isCredentialsNonExpired() {
        return !credentialsExpired;
    }

    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @spring.validator type="required"
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * @spring.validator type="required"
     * @spring.validator type="twofields" msgkey="errors.twofields"
     * @spring.validator-args arg1resource="user.password"
     * @spring.validator-args arg1resource="user.confirmPassword"
     * @spring.validator-var name="secondProperty" value="confirmPassword"
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * @spring.validator type="required"
     */
    public void setConfirmPassword(String confirmPassword) {
        this.confirmPassword = confirmPassword;
    }

    /**
     * @spring.validator type="required"
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * @spring.validator type="required"
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }


    /**
     * @spring.validator type="required"
     * @spring.validator type="email"
     */
    public void setEmail(String email) {
        this.email = email;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
    
    /**
     * @spring.validator type="required"
     */
    public void setPasswordHint(String passwordHint) {
        this.passwordHint = passwordHint;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
    
    public void setSpeeches(Set<?> speeches) {
        this.speeches = speeches;
    }
    
    public void setActors(Set<?> actors) {
        this.actors = actors;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }
    
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
    
    /**
     * Convert user roles to LabelValue objects for convenience.  
     */
    public List<LabelValue> getRoleList() {
        List<LabelValue> userRoles = new ArrayList<LabelValue>();

        if (this.roles != null) {
            for (Iterator<Role> it = roles.iterator(); it.hasNext();) {
                Role role = (Role) it.next();

                // convert the user's roles to LabelValue Objects
                userRoles.add(new LabelValue(role.getName(),
                                             role.getName()));
            }
        }

        return userRoles;
    }

    public void setAccountExpired(boolean accountExpired) {
        this.accountExpired = accountExpired;
    }
    
    public void setAccountLocked(boolean accountLocked) {
        this.accountLocked = accountLocked;
    }

    public void setCredentialsExpired(boolean credentialsExpired) {
        this.credentialsExpired = credentialsExpired;
    }

    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;

        final User user = (User) o;

        if (username != null ? !username.equals(user.getUsername()) : user.getUsername() != null) return false;

        return true;
    }

    public int hashCode() {
        return (username != null ? username.hashCode() : 0);
    }

    public String toString() {
        ToStringBuilder sb = new ToStringBuilder(this,
                ToStringStyle.DEFAULT_STYLE).append("username", this.username)
                .append("enabled", this.enabled)
                .append("accountExpired",this.accountExpired)
                .append("credentialsExpired",this.credentialsExpired)
                .append("accountLocked",this.accountLocked);

        GrantedAuthority[] auths = this.getAuthorities();
        if (auths != null) {
            sb.append("Granted Authorities: ");

            for (int i = 0; i < auths.length; i++) {
                if (i > 0) {
                    sb.append(", ");
                }
                sb.append(auths[i].toString());
            }
        } else {
            sb.append("No Granted Authorities");
        }
        return sb.toString();
    }
    
    public Speech getSpeechByProject(String projectName) {
        log.debug("Project Name = '" + projectName + "', user = " + this.username);
        Iterator<?> it = speeches.iterator();
        Speech speech = null;
        while(it.hasNext()) {
            Speech temp = (Speech) it.next();
            log.debug(this.username + " delivered speech on project '" + temp.getTMProject().getName() + "'");
            if(temp.getTMProject() != null 
                    && temp.getTMProject().getName().equalsIgnoreCase(projectName)) {
                speech = temp;
                break;
            }
        }
        
        return speech;
    }
    
    
    public Actor getActorByRole(String roleName) {
//        log.debug("roleName = " + roleName + ", user = " + this.username);
        Iterator<?> it = actors.iterator();
        Actor actor = null;
        while(it.hasNext()) {
            Actor temp = (Actor)it.next();
            if(temp.getTMRole().getName().contains(roleName)) {
                if (actor == null) {
                    actor = temp;
                } else {
                    if (temp.getAgenda() != null 
                        && temp.getAgenda().getDate() != null
                        && actor.getAgenda() != null
                        && actor.getAgenda().getDate() != null
                        && temp.getAgenda().getDate().after(actor.getAgenda().getDate())){
                        
                            actor = temp;
                    }
                }   
            }
        }
        
        return actor;
    }
    
    public Date[] getRoleAssignment() {
        String[] rolenames = Constants.ROLENAMES;
        Date[] dates = new Date[Constants.ROLENAMES.length];
        log.debug("before call getActorByRole() " + this.getStrActors());
        try {
            for(int i=0; i<rolenames.length; i++) {
                Actor actor = getActorByRole(rolenames[i]);
                if (actor != null && actor.getAgenda() != null) {
                    dates[i] = actor.getAgenda().getDate();
                } else {
                    dates[i] = null;
                }
            }
        }catch (Exception ex) {
            ex.printStackTrace();
        }
        return dates;
    }
    
    private String getStrActors() {
        StringBuffer sb = new StringBuffer();
        sb.append(" \n[ ").append(this.username).append(" ]: ").append(actors.size()).append(" actors\n");
        Iterator<?> it = actors.iterator();
        while(it.hasNext()) {
            sb.append("\t").append(((Actor)it.next()).toString()).append("\n");
        }
        return sb.toString();
    }
}
